package com.lingjtx.common.influx.client;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.text.csv.CsvUtil;
import com.lingjtx.common.core.util.StringUtil;
import com.lingjtx.common.influx.annotation.InfluxQuery;
import com.lingjtx.common.influx.annotation.Param;
import com.lingjtx.common.influx.common.InfluxMappedStatement;
import com.lingjtx.common.influx.common.ParameterBinder;
import com.lingjtx.common.influx.common.QueryType;

import java.lang.reflect.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class InfluxMapperProxy implements InvocationHandler {

    private final Class<?> mapperInterface;
    private final Map<String, InfluxMappedStatement> mappedStatements;
    private final InfluxQueryExecutor queryExecutor;

    public InfluxMapperProxy(Class<?> mapperInterface,
                             Map<String, InfluxMappedStatement> mappedStatements,
                             InfluxQueryExecutor queryExecutor) {
        this.mapperInterface = mapperInterface;
        this.mappedStatements = mappedStatements;
        this.queryExecutor = queryExecutor;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (isObjectMethod(method)) {
            return method.invoke(this, args);
        }

        String statementId = buildStatementId(method);
        InfluxMappedStatement ms = mappedStatements.get(statementId);
        if (ms == null) {
            throw new RuntimeException("No mapped statement for: " + statementId);
        }

        Map<String, Object> paramMap = buildParamMap(method, args);
        String parsedSql = ParameterBinder.bind(ms.getRawSql(), paramMap);

        InfluxQuery influxQuery = method.getAnnotation(InfluxQuery.class);
        return handleQuery(method, parsedSql, influxQuery);
    }

    // === 辅助方法 ===

    private boolean isObjectMethod(Method method) {
        return method.getDeclaringClass() == Object.class;
    }

    private String buildStatementId(Method method) {
        return mapperInterface.getName() + "." + method.getName();
    }

    /**
     * 构建参数 Map，支持 @Param 注解和参数名推断
     */
    private Map<String, Object> buildParamMap(Method method, Object[] args) {
        Map<String, Object> paramMap = new HashMap<>();
        if (args == null || args.length == 0) return paramMap;

        Parameter[] parameters = method.getParameters();
        for (int i = 0; i < parameters.length; i++) {
            Parameter parameter = parameters[i];
            Object arg = args[i];

            // 如果是 bean 或 map，可以展开
            if (arg != null && BeanUtil.isBean(arg.getClass())) {
                Map<String, Object> beanMap = BeanUtil.beanToMap(arg);
                paramMap.putAll(beanMap);
            } else if (arg instanceof Map<?, ?> map) {
                map.forEach((k, v) -> paramMap.put(String.valueOf(k), v));
            } else {
                // 使用 @Param 指定名称
                Param paramAnno = parameter.getAnnotation(Param.class);
                String paramName = (paramAnno != null) ? paramAnno.value() : "param" + i;
                paramMap.put(paramName, arg);
            }
        }
        return paramMap;
    }

    private Object handleQuery(Method method, String sql, InfluxQuery query) {
        if (method.getReturnType() == void.class) return null;

        if (QueryType.FLUX.equals(query.type())) {
            return handleFluxQuery(method, sql);
        } else if (QueryType.QL.equals(query.type())) {
            return queryExecutor.query(sql);
        }

        return null;
    }

    private Object handleFluxQuery(Method method, String sql) {
        String csv = queryExecutor.queryFlux(sql);
        if (StringUtil.isEmpty(csv)) return null;

        Class<?> returnType = method.getReturnType();
        if (returnType.isAssignableFrom(String.class)) {
            return csv;
        }

        if (returnType.isAssignableFrom(List.class)) {
            Type genericType = method.getGenericReturnType();
            if (genericType instanceof ParameterizedType pt) {
                Type itemType = pt.getActualTypeArguments()[0];
                return CsvUtil.getReader().read(csv, (Class<?>) itemType);
            }
        }

        throw new IllegalStateException("Unsupported return type for FLUX query: " + returnType);
    }
}